package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseAttrInfo;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.checkerframework.checker.units.qual.A;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    // 服务层调用product-client
    @Autowired
    private ProductFeignClient productFeignClient;

    // 需要获取到一个操作es的客户端
    // ElasticsearchRestTemplate 底层使用的是高级客户端
    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public void upperGoods(Long skuId) {
        // 通过productFeignClient获取到数据给Goods赋值

        Goods goods = new Goods();

        CompletableFuture<SkuInfo> skuInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
            goods.setId(skuInfo.getId());
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setPrice(skuInfo.getPrice().doubleValue());
            goods.setTitle(skuInfo.getSkuName());
            goods.setCreateTime(new Date());
            return skuInfo;
        });

        CompletableFuture<Void> categoryCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            goods.setCategory1Id(categoryView.getCategory1Id());
            goods.setCategory2Id(categoryView.getCategory2Id());
            goods.setCategory3Id(categoryView.getCategory3Id());
            goods.setCategory1Name(categoryView.getCategory1Name());
            goods.setCategory2Name(categoryView.getCategory2Name());
            goods.setCategory3Name(categoryView.getCategory3Name());
        });

        CompletableFuture<Void> tmCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {
            BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
            goods.setTmId(trademark.getId());
            goods.setTmName(trademark.getTmName());
            goods.setTmLogoUrl(trademark.getLogoUrl());
        });

        CompletableFuture<Void> attrCompletableFuture = CompletableFuture.runAsync(() -> {
            List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
            List<SearchAttr> searchAttrs = attrList.stream().map((baseAttrInfo -> {
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(baseAttrInfo.getId());
                searchAttr.setAttrName(baseAttrInfo.getAttrName());
                searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
                return searchAttr;
            })).collect(Collectors.toList());
            goods.setAttrs(searchAttrs);
        });

        CompletableFuture.allOf(skuInfoCompletableFuture,
                categoryCompletableFuture,
                tmCompletableFuture,
                attrCompletableFuture).join();

//        Goods goods = new Goods();
//        // sku基本信息
//        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
//        goods.setId(skuInfo.getId());
//        goods.setDefaultImg(skuInfo.getSkuDefaultImg());
//        goods.setPrice(skuInfo.getPrice().doubleValue());
//        goods.setTitle(skuInfo.getSkuName());
//        goods.setCreateTime(new Date());
//
//        // sku分类信息
//        BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
//        goods.setCategory1Id(categoryView.getCategory1Id());
//        goods.setCategory2Id(categoryView.getCategory2Id());
//        goods.setCategory3Id(categoryView.getCategory3Id());
//        goods.setCategory1Name(categoryView.getCategory1Name());
//        goods.setCategory2Name(categoryView.getCategory2Name());
//        goods.setCategory3Name(categoryView.getCategory3Name());
//
//        // sku的品牌信息
//        BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
//        goods.setTmId(trademark.getId());
//        goods.setTmName(trademark.getTmName());
//        goods.setTmLogoUrl(trademark.getLogoUrl());
//
//        // sku的平台属性
//        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
//        List<SearchAttr> searchAttrs = new ArrayList<>();
//        for (BaseAttrInfo baseAttrInfo : attrList) {
//            SearchAttr searchAttr = new SearchAttr();
//            searchAttr.setAttrId(baseAttrInfo.getId());
//            searchAttr.setAttrName(baseAttrInfo.getAttrName());
//            searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());
//        }
//        goods.setAttrs(searchAttrs);

        // 保存数据到es 上架
        goodsRepository.save(goods);
    }

    @Override
    public void lowerGoods(Long skuId) {
        // 删除
        goodsRepository.deleteById(skuId);
    }

    @Override
    public void incrHotScore(Long skuId) {
        // 采用ZSet
        String hotKey = "hotScore";
        // ZSet 自增
        Double score = redisTemplate.opsForZSet().incrementScore(hotKey, "skuId:" + skuId, 1);
        // 判断
        if (score % 10 == 0) {
            // 更新es
            Optional<Goods> optionalGoods = goodsRepository.findById(skuId);
            Goods goods = optionalGoods.get();
            goods.setHotScore(score.longValue());
            goodsRepository.save(goods);
        }
    }

    @Override
    public SearchResponseVo search(SearchParam searchParam) throws IOException {
        /*
            1. 先生成对应的dsl语句
            2. 执行dsl语句
            3. 将执行的结果集封装到SearchResponseVo对象中
         */
        SearchRequest searchRequest = this.buildQueryDsl(searchParam);
        // 执行dsl语句
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 封装结果集
        //trademarkList,attrsList,goodsList,total 会在 parseSearchResult 方法中赋值
        SearchResponseVo searchResponseVo = this.parseSearchResult(searchResponse);
        // 本质：给searchResponseVo这个对象中的七个属性赋值
        searchResponseVo.setPageNo(searchParam.getPageNo());
        searchResponseVo.setPageSize(searchParam.getPageSize());
        // 默认查看第一页，每页显示3条，如何计算总页数
        //Long totalPages = searchResponseVo.getTotal() % searchResponseVo.getPageSize() == 0 ? searchResponseVo.getTotal()/searchResponseVo.getPageSize() :searchResponseVo.getTotal()/searchResponseVo.getPageSize() + 1;
        long totalPages = (searchResponseVo.getTotal() + searchParam.getPageSize() - 1) / searchParam.getPageSize();
        searchResponseVo.setTotalPages(totalPages);
        // 返回结果
        return searchResponseVo;
    }

    private SearchResponseVo parseSearchResult(SearchResponse searchResponse) {
        //trademarkList,attrsList,goodsList,total 会在 parseSearchResult 方法中赋值
        SearchResponseVo searchResponseVo = new SearchResponseVo();
        // 获取最外层的hits
        SearchHits hits = searchResponse.getHits();
        // 获取内存的hits
        SearchHit[] subHits = hits.getHits();

        // 声明一个集合来存储goods
        List<Goods> goodsList = new ArrayList<>();
        // 循环遍历
        for (SearchHit subHit : subHits) {
            // 将source对应的字符串转换为goods
            String sourceAsString = subHit.getSourceAsString();
            // 获取到一个goods对象
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);
            // 细节处理：判断是否有高亮字段
            if (subHit.getHighlightFields().get("title") != null) {
                // 如果不为空获取里面的数据
                Text title = subHit.getHighlightFields().get("title").getFragments()[0];
                goods.setTitle(title.toString());
            }
            // 将goods添加到集合中
            goodsList.add(goods);

        }
        // 赋值goods集合
        searchResponseVo.setGoodsList(goodsList);

        // 品牌集合：从聚合中获取到数据
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();
        // Aggregation ParsedLongTerms
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> trademarkList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            // 给品牌赋值
            String tmId = bucket.getKeyAsString();
            tmVo.setTmId(Long.parseLong(tmId));

            // tmName
            ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmName(tmName);

            // tmLogoUrl
            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmLogoUrl(tmLogoUrl);

            return tmVo;
        }).collect(Collectors.toList());

        searchResponseVo.setTrademarkList(trademarkList);

        // 平台属性集合attrsList
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        // 获取桶中的数据
        List<SearchResponseAttrVo> attrsList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();

            // 给attrId赋值
            Number attrId = bucket.getKeyAsNumber();
            searchResponseAttrVo.setAttrId(attrId.longValue());

            // 给attrName赋值
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            // 属性名，同种只有一个值
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(attrName);

            // 给attrValue赋值
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            // 属性值，可能是一个集合
            List<String> attrValueList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            // 获取到平台属性值数据集合
            searchResponseAttrVo.setAttrValueList(attrValueList);

            return searchResponseAttrVo;
        }).collect(Collectors.toList());

        searchResponseVo.setAttrsList(attrsList);

        // 获取总条数
        searchResponseVo.setTotal(hits.getTotalHits().value);
        return searchResponseVo;
    }

    // 生成dsl语句 必须根据手撕dsl语句为参考
    private SearchRequest buildQueryDsl(SearchParam searchParam) {
        // 创建一个SearchSourceBuilder对象
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 创建一个bool对象
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 判断用户是否根据分类Id检索
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            // 创建一个filter --- term{ "category1Id" : "2"}
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            // 创建一个filter --- term{ "category2Id" : "13"}
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory3Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            // 创建一个filter --- term{ "category3Id" : "61"}
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }

        // 判断用户是否根据全文检索
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {
            // must --- match{title --> “小米手机" and}
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", searchParam.getKeyword()).operator(Operator.AND));
        }

        // 获取到品牌数据 trademark=2:华为
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            // 通过对字符串进行分割
            String[] split = trademark.split(":");
            // 得到分割之后的数组，对其进行判断
            if (split != null && split.length == 2) {
                // 获取品牌id bool -- filter --term
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", split[0]));
            }
        }

        // 通过平台属性值进行过滤 props=23:4G:运行内存&props=24:8G:机身内存
        String[] props = searchParam.getProps();
        if (props != null && props.length > 0) {
            // 循环遍历当前的数组
            for (String prop : props) {
                // prop=23:4G:运行内存 格式  是由:分割的
                String[] split = prop.split(":");
                if (split != null && split.length == 3) {
                    // 声明两个bool对象
                    BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
                    BoolQueryBuilder subQueryBuilder = QueryBuilders.boolQuery();
                    subQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                    subQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                    // 封装平台属性id平台属性值名称查询
                    queryBuilder.must(QueryBuilders.nestedQuery("attrs", subQueryBuilder, ScoreMode.None));
                    // 封装平台数组id 平台属性值名称查询赋值给外层bool
                    boolQueryBuilder.filter(queryBuilder);
                }
            }
        }

        // {query --- bool}
        searchSourceBuilder.query(boolQueryBuilder);

        // 设置高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style=color:red>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);

        // 设置分页
        int from = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        searchSourceBuilder.from(from);
        // 有个默认值
        searchSourceBuilder.size(searchParam.getPageSize());

        // 设置排序  1:hotScore 热度 2:price 价格
        // 前端传递的参数  order=1:desc | order=1:asc order=2:desc | order=2:asc
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            // 进行分割
            String[] split = order.split(":");
            if (split != null & split.length == 2) {
                // 声明一个字段记录按照哪种方式排序
                String field = "";
                // 判断是按照哪种方式进行排序
                switch (split[0]) {
                    case "1":
                        field = "hotScore";
                        break;
                    case "2":
                        field = "price";
                        break;
                }
                // 按照升序排列还是降序排列
                searchSourceBuilder.sort(field, "asc".equals(split[1]) ? SortOrder.ASC : SortOrder.DESC);
            } else {
                searchSourceBuilder.sort("hotScore", SortOrder.DESC);
            }
        }

        // 聚合 平台属性
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));

        // 聚合 品牌
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl")));

        // 其他的设置  查询的哪些字段显示
        searchSourceBuilder.fetchSource(new String[]{"id", "defaultImg", "title", "price"}, null);

        SearchRequest searchRequest = new SearchRequest("goods");
        String dsl = searchSourceBuilder.toString();
        System.out.println("dsl = \t" + dsl);
        searchRequest.source(searchSourceBuilder);
        //官网给的案例：通过案例可以看到所以的dsl语句都在此生存
//        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//        searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//        searchRequest.source(searchSourceBuilder);

        return searchRequest;
    }

    // 封装结果集练习
    private SearchResponseVo parseSearchResultExercise(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        // 获得外层hits
        SearchHits hits = searchResponse.getHits();

        // 获得内层hits
        SearchHit[] searchHits = hits.getHits();


        // 封装goodList

        // 创建goodsList
        List<Goods> goodsList = new ArrayList<>();
        // 遍历searchHits 取出数据赋值到goods
        for (SearchHit searchHit : searchHits) {
            String sourceAsString = searchHit.getSourceAsString();
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);
            if (searchHit.getHighlightFields().get("title") != null) {
                goods.setTitle(searchHit.getHighlightFields().get("title").getFragments()[0].toString());
            }
            goodsList.add(goods);
        }
        searchResponseVo.setGoodsList(goodsList);

        // 封装 品牌
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> tmVoList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();

            String keyAsString = bucket.getKeyAsString();
            tmVo.setTmId(Long.parseLong(keyAsString));

            ParsedStringTerms tmNameAgg = bucket.getAggregations().get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmName(tmName);

            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmLogoUrl(tmLogoUrl);

            return tmVo;
        }).collect(Collectors.toList());

        searchResponseVo.setTrademarkList(tmVoList);

        // 封装 平台属性
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<SearchResponseAttrVo> attrVoList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();

            String keyAsString = bucket.getKeyAsString();
            searchResponseAttrVo.setAttrId(Long.parseLong(keyAsString));

            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(attrName);

            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> attrValueList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(attrValueList);


            return searchResponseAttrVo;
        }).collect(Collectors.toList());

        searchResponseVo.setAttrsList(attrVoList);

        // 获得总数
        TotalHits totalHits = hits.getTotalHits();
        searchResponseVo.setTotal(totalHits.value);


        return searchResponseVo;
    }

    // 封装dsl语句练习
    private SearchRequest buildQueryDslExercise(SearchParam searchParam) {
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 分类检索入口
        if (!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id()));
        }
        if (!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }

        // 全文检索入口
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", searchParam.getKeyword()).operator(Operator.AND));


        // 根据品牌过滤  trademark=2:华为
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = trademark.split(":");
            if (split != null && split.length == 2) {
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", split[0]));
            }
        }

        // 根据平台属性过滤  props=23:4G:运行内存


        String[] props = searchParam.getProps();
        if (props != null && props.length > 0) {
            BoolQueryBuilder subQueryBuilder = QueryBuilders.boolQuery();
            for (String prop : props) {
                String[] split = prop.split(":");
                if(split != null && split.length == 3){
                    BoolQueryBuilder subBoolQueryBuilder = QueryBuilders.boolQuery();
                    subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
                    subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
                    subQueryBuilder.must(QueryBuilders.nestedQuery("attrs",subBoolQueryBuilder,ScoreMode.None));
                }
            }
            boolQueryBuilder.must(subQueryBuilder);
        }

        // 分页
        searchSourceBuilder.from((searchParam.getPageNo()-1)*searchParam.getPageSize());
        searchSourceBuilder.size(searchParam.getPageSize());

        // 排序 1:hotScore 2:price 前端传递的参数  order=1:desc | order=1:asc order=2:desc | order=2:asc
        String order = searchParam.getOrder();
        if(!StringUtils.isEmpty(order)){
            String[] split = order.split(":");
            if(split!=null && split.length ==2){
                String sortType = "";
                switch (split[0]){
                    case "1":
                        sortType = "hotScore";
                        break;
                    case "2":
                        sortType = "price";
                        break;
                }
                searchSourceBuilder.sort(sortType,"asc".equals(split[1])?SortOrder.ASC:SortOrder.DESC);
            }
        }

        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style:color=red>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);

        // 聚合 品牌
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                        .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                        .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl")));

        // 聚合 平台属性
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","attrs")
                        .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                                .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                                .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue"))));

        searchSourceBuilder.query(boolQueryBuilder);

        return searchRequest;
    }

    // 封装dsl语句练习2
    private SearchRequest buildQueryDslExercise2(SearchParam searchParam){
        SearchRequest searchRequest = new SearchRequest();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if(!StringUtils.isEmpty(searchParam.getCategory1Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }
        if(!StringUtils.isEmpty(searchParam.getCategory2Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id()));
        }
        if(!StringUtils.isEmpty(searchParam.getCategory3Id())) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }

        if (!StringUtils.isEmpty(searchParam.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("title",searchParam.getKeyword()).operator(Operator.AND));
        }
        // trademark=2:华为
        String trademark = searchParam.getTrademark();
        if(!StringUtils.isEmpty(trademark)){
            String[] split = trademark.split(":");
            if(split.length == 2){
                boolQueryBuilder.filter(QueryBuilders.termQuery("tmId",split[0]));
            }
        }

        //props=23:4G:运行内存
        //平台属性Id ，平台属性值名称,平台属性名

        String[] props = searchParam.getProps();
        BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
        for (String prop : props) {
            String[] split = prop.split(":");
            BoolQueryBuilder subBoolQueryBuilder = QueryBuilders.boolQuery();
            subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId",split[0]));
            subBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrValue",split[1]));
            boolQueryBuilder1.must(QueryBuilders.nestedQuery("attrs",subBoolQueryBuilder,ScoreMode.None));
        }
        boolQueryBuilder.filter(boolQueryBuilder1);

        // 分页
        searchSourceBuilder.from((searchParam.getPageNo()-1)*searchParam.getPageSize());
        searchSourceBuilder.size(searchParam.getPageSize());

        // 排序
        // 排序规则
        // 1:hotScore 2:price
        //private String order = ""; // 1：综合排序/热点  2：价格
        // 前端传递的参数  order=1:desc | order=1:asc order=2:desc | order=2:asc
        String order = searchParam.getOrder();
        String[] split = order.split(":");
        String orderType = "";
        switch (split[0]){
            case "1":
                orderType = "hotScore";
                break;
            case "2":
                orderType = "price";
                break;
        }
        searchSourceBuilder.sort(orderType,"asc".equals(split[1])?SortOrder.ASC:SortOrder.DESC);

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title");
        highlightBuilder.preTags("<span style:color=red>");
        highlightBuilder.postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);

        // 聚合 品牌
        searchSourceBuilder.aggregation(AggregationBuilders.terms("tmIdAgg").field("tmId")
                        .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                        .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogUrl")));

        // 聚合 平台属性
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrAgg","attrs")
                        .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrId")
                                .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrName"))
                                .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrValue"))));

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

    // 封装结果集练习2
    private SearchResponseVo parseSearchResultExercise2(SearchResponse searchResponse){
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        SearchHits hits = searchResponse.getHits();

        SearchHit[] searchHits = hits.getHits();
        List<Goods> goodsList = new ArrayList<>();
        for (SearchHit searchHit : searchHits) {
            String sourceAsString = searchHit.getSourceAsString();
            Goods goods = JSON.parseObject(sourceAsString, Goods.class);
            HighlightField title = searchHit.getHighlightFields().get("title");
            goods.setTitle(title.getFragments()[0].toString());
            goodsList.add(goods);
        }

        searchResponseVo.setGoodsList(goodsList);

        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();
        // 聚合 品牌

        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        List<SearchResponseTmVo> tmVoList = tmIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            String keyAsString = bucket.getKeyAsString();
            tmVo.setTmId(Long.parseLong(keyAsString));

            ParsedStringTerms aggregations = bucket.getAggregations().get("tmNameAgg");
            String tmName = aggregations.getBuckets().get(0).getKeyAsString();
            tmVo.setTmName(tmName);

            ParsedStringTerms tmLogoUrlAgg = bucket.getAggregations().get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();
            tmVo.setTmLogoUrl(tmLogoUrl);

            return tmVo;
        }).collect(Collectors.toList());
        searchResponseVo.setTrademarkList(tmVoList);

        // 聚合 平台属性
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<SearchResponseAttrVo> attrVoList = attrIdAgg.getBuckets().stream().map(bucket -> {
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            String keyAsString = bucket.getKeyAsString();
            searchResponseAttrVo.setAttrId(Long.parseLong(keyAsString));

            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            searchResponseAttrVo.setAttrName(attrName);

            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<String> stringList = attrValueAgg.getBuckets().stream().map(Terms.Bucket::getKeyAsString).collect(Collectors.toList());
            searchResponseAttrVo.setAttrValueList(stringList);

            return searchResponseAttrVo;
        }).collect(Collectors.toList());
        searchResponseVo.setAttrsList(attrVoList);


        TotalHits totalHits = hits.getTotalHits();
        searchResponseVo.setTotal(totalHits.value);

        return searchResponseVo;
    }
}
